﻿namespace Resonator.Core.Services
{
	using System;
	using System.Collections.Generic;
	using System.Globalization;
	using NHibernate;
	using NHibernate.Expressions;
	using NHibernate.SqlCommand;
	using Queries;
	using Resonator.Domain.Models;
	using Resonator.Domain.Services;

	public class ResourceResolver : IResourceResolver
	{
		private readonly IRepository<Resource> _resourceRepository;

		public ResourceResolver(IRepository<Resource> resourceRepository)
		{
			if( resourceRepository == null )
			{
				throw new ArgumentNullException( "resourceRepository" );
			}

			_resourceRepository = resourceRepository;
		}

		public virtual IList<ResolveResult> Resolve(IEnumerable<Resource> resources, IEnumerable<CultureInfo> requiredCultures)
		{
			List<ResolveResult> results = new List<ResolveResult>();

			if( resources == null || requiredCultures == null )
			{
				return results;
			}

			Dictionary<string, Resource> resourcesToResolve = new Dictionary<string, Resource>();

			QueryBuilder<Resource> searchCriteria = null;
			bool queryInitialized = false;

			foreach( Resource resource in resources )
			{
				resourcesToResolve[resource.Phrase] = resource;
				if( !queryInitialized )
				{
					queryInitialized = true;
					searchCriteria = Where.Resource.Phrase == resource.Phrase;
				}
				else
				{
					searchCriteria = searchCriteria || Where.Resource.Phrase == resource.Phrase;
				}
			}

			if( !queryInitialized )
			{
				return results;
			}

			List<int> lcids = new List<int>();
			foreach( CultureInfo requiredCulture in requiredCultures )
			{
				lcids.Add( requiredCulture.LCID );
			}

			searchCriteria = searchCriteria && Where.Resource.Translations.With( JoinType.LeftOuterJoin, FetchMode.Join ).AddCriterion( Expression.In( "LCID", lcids ) );

			// Query DB for exact matches
			Resource[] resourcesFromDb = _resourceRepository.FindAll( searchCriteria );

			foreach( Resource resourceFromDb in resourcesFromDb )
			{
				Resource resourceToResolve;
				if( !resourcesToResolve.TryGetValue( resourceFromDb.Phrase, out resourceToResolve ) )
				{
					throw new ResonatorException( "We queried for a Resource that we didn't need '{0}'", resourceFromDb.Phrase );
				}

				resourcesToResolve.Remove( resourceToResolve.Phrase );
				results.Add( ResolveResult.Create( resourceToResolve, requiredCultures, resourceFromDb ) );
			}

			// Query FT for Resources with no exact match
			foreach( Resource resourceToResolve in resourcesToResolve.Values )
			{
				IList<Resource> partialMatches = _resourceRepository.Search( resourceToResolve.Phrase );
				if( partialMatches.Count > 0 )
				{
					results.Add( ResolveResult.Create( resourceToResolve, requiredCultures, partialMatches ) );
				}
				else
				{
					results.Add( ResolveResult.Create( resourceToResolve ) );
				}
			}

			return results;
		}
	}
}